/* ----------------------------------------------------------------------------
* File: thread_wrapper.cpp
*
* Desc: Interface for thread
*
* Created: 27/06/2011, by Alexey Korshunov
* ---------------------------------------------------------------------------- */
#include "thread_wrapper.h"

#define WAIT_STOP_THREAD	5000	//5 secs

namespace Common
{
	ThreadWrapper::ThreadWrapper()
		: m_hThread(NULL)
	{
		m_hEventThreadStart = ::CreateEvent(NULL, TRUE, FALSE, NULL);
		m_hEventThreadOver = ::CreateEvent(NULL, TRUE, FALSE, NULL);
		m_hEventUserAbort = ::CreateEvent(NULL, TRUE, FALSE, NULL);
	}

	ThreadWrapper::~ThreadWrapper()
	{
		::CloseHandle(m_hEventThreadStart);
		::CloseHandle(m_hEventThreadOver);
		::CloseHandle(m_hEventUserAbort);
	}

	bool ThreadWrapper::StartThread()
	{
		if (m_hThread != NULL)
			return false;

		DWORD dwThreadId;
		m_hThread = CreateThread(NULL, 0, __ThreadProxyProc, (LPVOID)this, 0, &dwThreadId);
		if (!m_hThread)
			return false;

		// wait thread start
		DWORD dwWait = ::WaitForSingleObject(m_hEventThreadStart, INFINITE);
		::ResetEvent(m_hEventThreadStart);

		if (dwWait != WAIT_OBJECT_0)
			return false;

		return true;
	}

	void ThreadWrapper::WaitThread()
	{
		if (::WaitForSingleObject(m_hEventThreadOver, INFINITE) == WAIT_OBJECT_0)
			::ResetEvent(m_hEventThreadOver);
	}

	void ThreadWrapper::StopThread()
	{
		if (m_hThread != NULL)
		{
			::SetEvent(m_hEventUserAbort);

			// default wait 5 secs
			if (::WaitForSingleObject(m_hEventThreadOver, WAIT_STOP_THREAD) == WAIT_OBJECT_0)
			{
				// ok . 			
				::ResetEvent(m_hEventThreadOver);
			}
			else  
			{	
				// kill thread, but it is dangerous
				::TerminateThread(m_hThread, 1);
				m_hThread = NULL;
			}	
		}
	}

	DWORD __stdcall ThreadWrapper::__ThreadProxyProc(LPVOID lpParam)
	{
		ThreadWrapper* wrapper = reinterpret_cast<ThreadWrapper*>(lpParam);
		wrapper->BeforeThread();

		try
		{
			wrapper->Run();
		}
		catch(...)
		{
			
		}
		
		wrapper->AfterThread();
		::SetEvent(wrapper->m_hEventThreadOver);
		return 0;
	}

	void ThreadWrapper::BeforeThread()
	{
		::SetEvent(m_hEventThreadStart);
	}

	void ThreadWrapper::AfterThread()
	{
		::CloseHandle(m_hThread);
		m_hThread = NULL;
	}

	bool ThreadWrapper::QueryExitStatus(DWORD dwDuration /*= 0*/)
	{
		DWORD dwWake = ::WaitForSingleObject(m_hEventUserAbort, dwDuration);
		if (dwWake == WAIT_OBJECT_0)
		{
			::ResetEvent(m_hEventUserAbort);
			return true;
		}

		return false;
	}
}